// Flyweight factory.
// Copyright 2006 (C) Ralph Thomas

#include <view/gtk/flyweight/parent.h>
#include <view/gtk/flyweight/cell-renderer-icon.h>
#include <rb-cell-renderer-rating.h>
#include <rb-stock-icons.h>
#include <util/resources.h>
#include <factory.hpp>

#include <iostream>

namespace bfs = boost::filesystem;

namespace view {
    //
    // The flyweight parent does almost all of the hard work involving flyweights,
    // so we can build all of the different flyweights here without too much code.
    //
    struct flyweight_view_t {
        GtkCellRenderer*	flyweight_m;
        GtkWidget*		parent_m;
        flyweight_parent*	fwp_m;

        flyweight_view_t(GtkCellRenderer* flyweight, GtkWidget* widget, flyweight_parent* fwp)
         : flyweight_m(flyweight), parent_m(widget), fwp_m(fwp) {}

        void measure(adobe::extents_t& extents) {
            int width, height;
            gtk_cell_renderer_get_size(flyweight_m, parent_m, 0, 0, 0, &width, &height);
            extents.width() = width;
            extents.height() = height;
        }
        void place(const adobe::place_data_t& place_data) {
            g_object_set(flyweight_m, "xpad", 0, "ypad", 0, "width", place_data.horizontal().length_m, "height", place_data.vertical().length_m, NULL);
            fwp_m->position_renderer(flyweight_m, place_data.horizontal().length_m, place_data.vertical().length_m, place_data.horizontal().position_m, place_data.vertical().position_m);
        }
    };
    void insert_flyweight(flyweight_parent* fwp, GtkWidget* widget, GtkCellRenderer* target, const adobe::dictionary_t& params, const adobe::factory_token_t& token, const adobe::eve_t::iterator& parent) {
        adobe::any_placeable_t* p = new adobe::any_placeable_t(flyweight_view_t(target, widget, fwp));
        token.client_holder_m.assemblage_m.delete_on_destruction(p);
        token.client_holder_m.eve_m.add_view_element( parent,
					adobe::layout_attributes_t(),
					false,
					params,
					boost::ref(*p)
				);
    }
    static void rated(GtkCellRenderer* r, const char* path, double rating, gpointer data) {
        flyweight_parent* fwp(reinterpret_cast<flyweight_parent*>(data));
        if (!fwp) return;
        fwp->set(adobe::any_regular_t(rating));
    }
    adobe::widget_node_t flyweight_rating_factory(
            const adobe::dictionary_t&      parameters,
            const adobe::widget_node_t&     parent,
            const adobe::factory_token_t&   token)
    {
        //
        // We have to be a flyweight child.
        //
        flyweight_parent* real_parent = static_cast<flyweight_parent*>( g_object_get_data(G_OBJECT(parent.display_token_m), "flyweight_parent" ) );
        if (!real_parent) throw std::runtime_error( "fw_rating is a flyweight widget, and may only be a child of a flyweight container!" );
        //
        // Make sure that we have the pixmaps this view requires loaded.
        //
        bfs::path icon_path = util::getResourcePath() / "images";
		rb_stock_icons_init( icon_path.native_file_string().c_str() );
		//
		// Now create the cell renderer.
		//
		GtkCellRenderer* r = rb_cell_renderer_rating_new();
		real_parent->adopt_renderer(r);

		g_signal_connect(r, "rated", G_CALLBACK(&rated), real_parent);
		//
		// Add the renderer to the layout.
		//
		insert_flyweight(real_parent, real_parent->get_widget(), r, parameters, token, parent.eve_token_m);
		//
		// Give the parent our datapump.
		//
		real_parent->adopt_datapump(r, "rating", parameters.at(adobe::static_name_t("bind")));
		//
		// That's all.
		//
		return parent;
    }
	adobe::widget_node_t flyweight_icon_factory(
			const adobe::dictionary_t&		parameters,
			const adobe::widget_node_t&		parent,
			const adobe::factory_token_t&	token)
	{
		//
		// We have to be a flyweight child.
		//
	        flyweight_parent* real_parent = static_cast<flyweight_parent*>( g_object_get_data(G_OBJECT(parent.display_token_m), "flyweight_parent" ) );
		if (!real_parent) throw std::runtime_error( "fw_icon is a flyweight widget, and may only be a child of a flyweight container!" );
		//
		// Create the icon cell renderer.
		//
		GtkCellRenderer* r = mission_cell_renderer_icon_new();
		mission_cell_renderer_icon_set_notify_function(r, boost::bind( &flyweight_parent::redraw, real_parent, _1 ) );
		real_parent->adopt_renderer(r);
		real_parent->subscribe_index(r, "index");
		//
		// Add the renderer to the layout.
		//
		insert_flyweight(real_parent, real_parent->get_widget(), r, parameters, token, parent.eve_token_m);
		//
		// Give the parent our icon name datapump.
		//
		real_parent->adopt_datapump(r, "icon-name", parameters.at(adobe::static_name_t("bind")));
		//
		// Cause the parent to invalidate whenever an icon gets
		// regenerated.
		//
		token.client_holder_m.assemblage_m.hold_connection(mission_cell_renderer_icon_inval(boost::bind(&flyweight_parent::redraw_all, real_parent)));
		//
		// That's all.
		//
		return parent;
	}
}
